package main;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.DefaultFitnessEvaluator;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.InvalidConfigurationException;
import org.jgap.event.EventManager;
import org.jgap.impl.CrossoverOperator;
import org.jgap.impl.IntegerGene;
import org.jgap.impl.StockRandomGenerator;
import org.jgap.impl.SwappingMutationOperator;
import org.jgap.impl.TournamentSelector;

public class AppMain {

	private static Integer evolutions;
	private static Integer popSize;
	/**
	 * @param args
	 * @throws InvalidConfigurationException 
	 * @throws IOException 
	 */
	public static void main(String[] args) throws InvalidConfigurationException, IOException {
		//Config del proyecto
		Configuration conf = new Configuration();
		//Seteo la poblacion inicial
		conf.setPopulationSize(getPopulationSize(args));
		//Selecciono el manager que nunca use
		conf.setEventManager(new EventManager());
		//Evaluador usado por los metodos de seleccion
		conf.setFitnessEvaluator(new DefaultFitnessEvaluator());
		//En promedio funciona un poco mejor con WeightedRouletteSelector
//		conf.setFitnessEvaluator(new DeltaFitnessEvaluator());
		//La funcion de fitness
		conf.setFitnessFunction(new SudokuFitness());
		
		//Armo los genes Cada cromosoma es un sudoku tiene 81 genes en este caso porque considero fijo uno de 9x9
		Gene[] gens = new Gene[81];
		for(int i =0 ; i< 81;i++)
			gens[i] = new IntegerGene(conf,0,9);
		
		//Restricciones a mano 
		gens[0] = new IntegerGene(conf,4,4);
		gens[8] = new IntegerGene(conf,8,8);
		gens[80] = new IntegerGene(conf,2,2);
		gens[72] = new IntegerGene(conf,1,1);
		gens[28] = new IntegerGene(conf,5,5);
		
		//Armo el chromosoma
		Chromosome chrome = new Chromosome(conf,gens);
		
		//Prototype 
		conf.setSampleChromosome(chrome);
		
		//Agrego los operadores y selectores
		conf.setRandomGenerator(new StockRandomGenerator());
		conf.addGeneticOperator(new CrossoverOperator(conf,2));
//		conf.addNaturalSelector(new WeightedRouletteSelector(conf), true);
		//Parece funcionar mejor que el WeightedRouletteSelector
		conf.addNaturalSelector(new TournamentSelector(conf, 32, 0.25),true);
		conf.addGeneticOperator(new SwappingMutationOperator(conf,1000));
		
		//Inicio una poblacio inicial con valores random
		Genotype population = Genotype.randomInitialGenotype(conf);
		
		//Aplica seleccion y operadores.
		System.out.println("Start !!! at: "+ new Date());
		population.evolve(getEvolutions(args));
		
		printBestFitness(population);
		
	}

	public static void printBestFitness(Genotype population)throws IOException {
		Gene[] gens;
		//Obtengo los 10 mejores
		//TODO: Que traiga varios y distintos!.
		BufferedWriter output = new BufferedWriter(new FileWriter(new File("../sudoResult.txt_"+new Date())));	
		output.write("Initial Population size: "+popSize+"\n");
		output.write("Iterations: "+evolutions+"\n");
		output.write(new Date().toString());
		output.newLine();
		List<Chromosome> bestChromosomes = population.getFittestChromosomes(10);		
		for(Chromosome a_chrom : bestChromosomes){
			int totalGens=0;
			gens = a_chrom.getGenes();
			for(int i = 0; i < 9; i++){				
				for(int j = 0;j < 9; j++){
						output.write("  "+gens[j+totalGens].getAllele()+" ");
				}
				totalGens += 9;
				output.newLine();
			}			
			output.newLine();		
			output.write("  Fitness: "+(Math.round(a_chrom.getFitnessValue())));
			output.newLine();
			output.newLine();
		}		
		output.close();
		System.out.println("Finish at: "+new Date());
	}
	
	public static Integer getPopulationSize(String[] args){
		if(popSize == null)
			popSize= args.length > 0 ? Integer.parseInt(args[0]) : 0;
			
			return popSize;
	}
	
	public static Integer getEvolutions(String[] args){		
		if (evolutions == null)
			evolutions = args.length > 1 ? Integer.parseInt(args[1]) : 0;

		return evolutions;
	}

}
